#ifndef _GRO_INPUT_H__
#define _GRO_INPUT_H__

#include "GRO_Gui.h"

namespace GRO
{
    
    
    
    // ACTIONS, EVENTS, INPUT, CURSORS
    
    
    enum ActionEnum
    {
        ACTION_NONE,
        ACTION_PRESS,
        ACTION_ACTIVATE,
        ACTION_MOTION,
        ACTION_MOTION_UP,
        ACTION_MOTION_DOWN,
        ACTION_MOTION_LEFT,
        ACTION_MOTION_RIGHT,
        ACTION_HOVER,
        ACTION_ENTER,
        ACTION_LEAVE,
        ACTION_CANCEL,
        ACTION_CLOSE,
        ACTION_MAXIMIZE,
        ACTION_SCROLL,
        ACTION_DRAG
    };
    
    class Widget;
    
    class EventResult
    {
        public:
        Widget* widget;  // This could be NULL if no widget could be activated
        // but the event still belonged to the InputMethod.
        bool caught;
        EventResult()
                : widget(NULL)
                , caught(false)
        {}
        EventResult(Widget* widget, bool caught)
                : widget(widget)
                , caught(caught)
        {}
    };
    
    // Put mouse, keyboard, joystick under one base class?
    // Then I can just hold an input method for each player and use the common
    // functions to work with the gui.
    class InputMethod
    {
        // up, down, left, right
        // button1, button2 (click, right-click)
        public:
        InputMethod()
        {}
        
        virtual void updateState()
        {}
        
        // Returns the widget if this InputMethod caught the event.  Otherwise, the
        // event doesn't belong to this InputMethod.
        //virtual EventResult event(const SDL_Event& event) = 0;
    };
    
    
    
    // This is a container for a widget, surface, or string that is used by the
    // clipboard and by the drag and drop system.
    class CopyItem
    {
        public:
        Widget* widget;
        SDL_Surface* surface;
        string str;
        
        CopyItem()
            : widget(NULL)
            , surface(NULL)
        {}
        
        CopyItem(Widget* widget)
            : widget(widget)
            , surface(NULL)
        {}
        
        CopyItem(SDL_Surface* surface)
            : widget(NULL)
            , surface(surface)
        {}
        
        CopyItem(string str)
            : widget(NULL)
            , surface(NULL)
            , str(str)
        {}
        
        bool hasWidget()
        {
            return !(widget == NULL);
        }
        
        bool hasSurface()
        {
            return !(surface == NULL);
        }
        
        bool hasString()
        {
            return !(str == "");
        }
        
    };
    
    
    
    class Mouse : public InputMethod
    {
        public:
        //Uint8 index;
        int x, y;
        int dx, dy;
        
        bool left;
        bool middle;
        bool right;
        
        Widget* motionFocus;
        Widget* scrollFocus;
        Widget* buttonFocus;
        
        Mouse()
                : x(0)
                , y(0)
                , dx(0)
                , dy(0)
                , left(0), middle(0), right(0)
                , motionFocus(NULL)
                , scrollFocus(NULL)
                , buttonFocus(NULL)
        {}
        
        void updateState()
        {
            dx = x;
            dy = y;
            Uint8 buttons = SDL_GetMouseState(&x, &y);
            dx = x - dx;
            dy = y - dy;
            left = buttons & SDL_BUTTON_LEFT;
            middle = buttons & SDL_BUTTON_MIDDLE;
            right = buttons & SDL_BUTTON_RIGHT;
        }
        
        /*EventResult event(const SDL_Event& event)
        {
            if(event.type == SDL_MOUSEMOTION)
            {
                x = event.motion.x;
                y = event.motion.y;
                dx = event.motion.xrel;
                dy = event.motion.yrel;
                left = event.motion.state & SDL_BUTTON_LEFT;
                middle = event.motion.state & SDL_BUTTON_MIDDLE;
                right = event.motion.state & SDL_BUTTON_RIGHT;
                return EventResult(motionFocus, true);
            }
            else if(event.type == SDL_MOUSEBUTTONDOWN)
            {
                x = event.button.x;
                y = event.button.y;
                
                if(event.button.button == SDL_BUTTON_WHEELDOWN)
                {
                    // Call widget scroll function (if wheel action is set to that)
                    // Should I check with InRect here first?  Then the widget's scroll function
                    // can be used by both Cursors (x,y) and Navigators (no x,y).
                    return EventResult(scrollFocus, true);
                }
                else if(event.button.button == SDL_BUTTON_WHEELUP)
                {
                    // Call widget scroll function (if wheel action is set to that)
                    return EventResult(scrollFocus, true);
                }
                else if(event.button.button == SDL_BUTTON_LEFT)
                {
                    left = true;
                    // Call widget function
                }
                else if(event.button.button == SDL_BUTTON_MIDDLE)
                {
                    middle = true;
                    // Call widget function
                }
                else if(event.button.button == SDL_BUTTON_RIGHT)
                {
                    right = true;
                    // Call widget function
                }
                
                return EventResult(buttonFocus, true);
            }
            else if(event.type == SDL_MOUSEBUTTONUP)
            {
                x = event.button.x;
                y = event.button.y;
                
                if(event.button.button == SDL_BUTTON_LEFT)
                {
                    left = false;
                    // Call widget function
                }
                else if(event.button.button == SDL_BUTTON_MIDDLE)
                {
                    middle = false;
                    // Call widget function
                }
                else if(event.button.button == SDL_BUTTON_RIGHT)
                {
                    right = false;
                    // Call widget function
                }
                return EventResult(buttonFocus, true);
            }
            return EventResult();
        }*/
    };
    
    class Joystick : public InputMethod
    {
        public:
        Uint8 index;
        SDL_Joystick* joystick;
        bool closeJoystick;
        string name;
        
        Uint8 numAxes;
        Uint8 numButtons;
        Uint8 numHats;
        Uint8 numBalls;
        
        Sint16* axisValue;
        Uint8* buttonValue;  // SDL_PRESSED or SDL_RELEASED
        Uint8* hatValue;  // enum
        // Does SDL_JoystickGetBall() overlap with the events?
        //Sint16* ballX;
        //Sint16* ballY;
        int* ballRelX;  // relative
        int* ballRelY;  // relative
        
        // bool* softAxis; // tells if the axis should be treated as a joystick rather than a d-pad
        
        ActionEnum* axisAction;  // all ACTION_NAV
        ActionEnum* buttonAction; // ACTIVATE, CANCEL, CLOSE, etc
        ActionEnum* hatAction;
        ActionEnum* ballAction;
        
        Widget* motionFocus;
        Widget* scrollFocus;
        Widget* buttonFocus;
        
        Joystick()
                : index(0)
                , joystick(NULL)
                , closeJoystick(0)
                , numAxes(0)
                , numButtons(0)
                , numHats(0)
                , numBalls(0)
                , axisValue(NULL)
                , buttonValue(NULL)
                , hatValue(NULL)
                //, ballX(NULL)
                //, ballY(NULL)
                , ballRelX(NULL)
                , ballRelY(NULL)
                , axisAction(NULL)
                , buttonAction(NULL)
                , hatAction(NULL)
                , ballAction(NULL)
                , motionFocus(NULL)
                , scrollFocus(NULL)
                , buttonFocus(NULL)
        {}
        Joystick(SDL_Joystick* joystick)
                : index(0)
                , joystick(joystick)
                , closeJoystick(0)
                , numAxes(0)
                , numButtons(0)
                , numHats(0)
                , numBalls(0)
                , axisValue(NULL)
                , buttonValue(NULL)
                , hatValue(NULL)
                //, ballX(NULL)
                //, ballY(NULL)
                , ballRelX(NULL)
                , ballRelY(NULL)
                , axisAction(NULL)
                , buttonAction(NULL)
                , hatAction(NULL)
                , ballAction(NULL)
                , motionFocus(NULL)
                , scrollFocus(NULL)
                , buttonFocus(NULL)
        {
            int i = SDL_JoystickIndex(joystick);
            if(i < 0)
            {
                joystick = NULL;
                return;
            }
            index = i;
            
            name = SDL_JoystickName(index);
            
            numAxes = SDL_JoystickNumAxes(joystick);
            numButtons = SDL_JoystickNumButtons(joystick);
            numHats = SDL_JoystickNumHats(joystick);
            numBalls = SDL_JoystickNumBalls(joystick);
            
            axisValue = new Sint16[numAxes];
            for(int i = 0; i < numAxes; i++)
                axisValue[i] = 0;
            buttonValue = new Uint8[numButtons];
            for(int i = 0; i < numButtons; i++)
                buttonValue[i] = 0;
            hatValue = new Uint8[numHats];
            for(int i = 0; i < numHats; i++)
                hatValue[i] = 0;
            //ballX = new Sint16[numBalls];
            //ballY = new Sint16[numBalls];
            ballRelX = new int[numBalls];
            ballRelY = new int[numBalls];
            for(int i = 0; i < numBalls; i++)
            {
                ballRelX[i] = 0;
                ballRelY[i] = 0;
            }
            
            axisAction = new ActionEnum[numAxes];
            buttonAction = new ActionEnum[numButtons];
            hatAction = new ActionEnum[numHats];
            ballAction = new ActionEnum[numBalls];
            
            for(int i = 0; i < numAxes; i++)
                axisAction[i] = ACTION_DRAG;
            if(numAxes >= 1)
            {
                axisAction[0] = ACTION_MOTION_LEFT;
            }
            if(numAxes >= 2)
            {
                axisAction[1] = ACTION_MOTION_UP;
            }
            for(int i = 0; i < numButtons; i++)
                buttonAction[i] = ACTION_ACTIVATE;
            for(int i = 0; i < numHats; i++)
                hatAction[i] = ACTION_SCROLL;
            for(int i = 0; i < numBalls; i++)
                ballAction[i] = ACTION_DRAG;
        }
        Joystick(Uint8 index)
                : index(index)
                , joystick(NULL)
                , closeJoystick(0)
                , numAxes(0)
                , numButtons(0)
                , numHats(0)
                , numBalls(0)
                , axisValue(NULL)
                , buttonValue(NULL)
                , hatValue(NULL)
                //, ballX(NULL)
                //, ballY(NULL)
                , ballRelX(NULL)
                , ballRelY(NULL)
                , motionFocus(NULL)
                , scrollFocus(NULL)
                , buttonFocus(NULL)
        {
            joystick = SDL_JoystickOpen(index);
            if(joystick == NULL)
            {
                index = 0;
                return;
            }
            
            name = SDL_JoystickName(index);
            
            numAxes = SDL_JoystickNumAxes(joystick);
            numButtons = SDL_JoystickNumButtons(joystick);
            numHats = SDL_JoystickNumHats(joystick);
            numBalls = SDL_JoystickNumBalls(joystick);
            
            axisValue = new Sint16[numAxes];
            buttonValue = new Uint8[numButtons];
            hatValue = new Uint8[numHats];
            //ballX = new Sint16[numBalls];
            //ballY = new Sint16[numBalls];
            ballRelX = new int[numBalls];
            ballRelY = new int[numBalls];
            
            axisAction = new ActionEnum[numAxes];
            buttonAction = new ActionEnum[numButtons];
            hatAction = new ActionEnum[numHats];
            ballAction = new ActionEnum[numBalls];
            
            for(int i = 0; i < numAxes; i++)
                axisAction[i] = ACTION_DRAG;  // needs action_motion_x and y, only for this, not for widgets.
            for(int i = 0; i < numButtons; i++)
                buttonAction[i] = ACTION_ACTIVATE;  // This should be able to have motion, too...  motion_left, right, etc.?
            for(int i = 0; i < numHats; i++)
                hatAction[i] = ACTION_SCROLL;
            for(int i = 0; i < numBalls; i++)
                ballAction[i] = ACTION_DRAG;
        }
        virtual ~Joystick()
        {
            if(closeJoystick)
                SDL_JoystickClose(joystick);
            delete[] axisValue;
            delete[] buttonValue;
            delete[] hatValue;
            delete[] ballRelX;
            delete[] ballRelY;
        }
        
        void updateState()
        {
            for(int i = 0; i < numAxes; i++)
            {
                axisValue[i] = SDL_JoystickGetAxis(joystick, i);
            }
            for(int i = 0; i < numButtons; i++)
            {
                buttonValue[i] = SDL_JoystickGetButton(joystick, i);
            }
            for(int i = 0; i < numHats; i++)
            {
                hatValue[i] = SDL_JoystickGetHat(joystick, i);
            }
            for(int i = 0; i < numBalls; i++)
            {
                SDL_JoystickGetBall(joystick, i, &ballRelX[i], &ballRelY[i]);
            }
        }
        
        /*EventResult axisEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYAXISMOTION || event.jaxis.which != index || joystick == NULL)
                return EventResult();
            Uint8 axis = event.jaxis.axis;
            if(axis >= numAxes)
                return EventResult(NULL, true);
            axisValue[axis] = event.jaxis.value;
            // call widget function
            switch(axisAction[axis])
            {
                case ACTION_DRAG:
                    return EventResult(motionFocus, true);
                case ACTION_ACTIVATE:
                    return EventResult(buttonFocus, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult buttonEvent(const SDL_Event& event)
        {
            if((event.type != SDL_JOYBUTTONDOWN && event.type != SDL_JOYBUTTONUP || joystick == NULL)
               || event.jbutton.which != index)
                return EventResult();
            Uint8 button = event.jbutton.button;
            if(button >= numButtons)
                return EventResult(NULL, true);
            buttonValue[button] = event.jbutton.state;
            // call widget function
            switch(buttonAction[button])
            {
                case ACTION_DRAG:
                    return EventResult(motionFocus, true);
                case ACTION_ACTIVATE:
                    return EventResult(buttonFocus, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult hatEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYHATMOTION || event.jhat.which != index || joystick == NULL)
                return EventResult();
            Uint8 hat = event.jhat.hat;
            if(hat >= numHats)
                return EventResult(NULL, true);
            hatValue[hat] = event.jhat.value;
            // call widget function
            switch(hatAction[hat])
            {
                case ACTION_DRAG:
                    return EventResult(motionFocus, true);
                case ACTION_ACTIVATE:
                    return EventResult(buttonFocus, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult ballEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYBALLMOTION || event.jball.which != index || joystick == NULL)
                return EventResult();
            Uint8 ball = event.jball.ball;
            if(ball >= numBalls)
                return EventResult(NULL, true);
            ballRelX[ball] = event.jball.xrel;
            ballRelY[ball] = event.jball.yrel;
            // call widget function
            switch(ballAction[ball])
            {
                case ACTION_DRAG:
                    return EventResult(motionFocus, true);
                case ACTION_ACTIVATE:
                    return EventResult(buttonFocus, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult event(const SDL_Event& event)
        {
            switch(event.type)
            {
                case SDL_JOYAXISMOTION:
                    return axisEvent(event);
                case SDL_JOYHATMOTION:
                    return hatEvent(event);
                case SDL_JOYBALLMOTION:
                    return ballEvent(event);
                case SDL_JOYBUTTONDOWN:
                case SDL_JOYBUTTONUP:
                    return buttonEvent(event);
                default:
                    return EventResult();
            }
        }*/
        
    };
    
    class Keyboard : public InputMethod
    {
        public:
        Uint8* keystates;
        SDLKey key;
        SDLMod mod;
        Uint16 unicode;
        
        Widget* focus;
        
        Keyboard()
        {
            init();
        }
        
        void init()
        {
            keystates = SDL_GetKeyState(NULL);
        }
        
        void updateState()
        {
            mod = SDL_GetModState();
        }
        /*
        EventResult event(const SDL_Event& event)
        {
            if(event.type == SDL_KEYDOWN)
            {
                key = event.key.keysym.sym;
                mod = event.key.keysym.mod;
                unicode = event.key.keysym.unicode;
                // Call widget function
                return EventResult(focus, true);
            }
            else if(event.type == SDL_KEYUP)
            {
                key = event.key.keysym.sym;
                mod = event.key.keysym.mod;
                unicode = event.key.keysym.unicode;
                // Call widget function
                return EventResult(focus, true);
            }
            return EventResult();
        }*/
    };
    
    class Cursor
    {
        public:
        Gui* gui;
        
        Cursor()
                : gui(NULL)
        {}
        
        virtual EventResult event(const SDL_Event& event) = 0;
        virtual void update() = 0;
        virtual void draw() = 0;
    };
    
    enum ScrollSelectEnum
    {
        SCROLL_BUTTON_FOCUS,
        SCROLL_HOVER_FOCUS,
        SCROLL_LEFT_CLICK,
        SCROLL_MIDDLE_CLICK,
        SCROLL_RIGHT_CLICK
    };
    
    class InputButtonSettings
    {
        public:
        
        Uint16 button;
        ActionEnum downAction;
        ActionEnum upAction;
        Widget* currentWidget;
        bool panelFocus; // Make the panel come forward
        bool panelGrabbable; // Let panels grab the clicks
        DirectionEnum scrollDirection;
        bool useMotion;
        bool useDrag;
        bool pressed;
        CopyItem* drag;
        
        InputButtonSettings()
                : button(0)
                , downAction(ACTION_PRESS)
                , upAction(ACTION_ACTIVATE)
                , currentWidget(NULL)
                , panelFocus(false)
                , panelGrabbable(false)
                , scrollDirection(DOWN)
                , useMotion(true)
                , useDrag(true)
                , pressed(false)
                , drag(NULL)
        {}
        InputButtonSettings(ActionEnum downAction, ActionEnum upAction, bool panelFocus = false, bool panelGrabbable = false)
                : button(0)
                , downAction(downAction)
                , upAction(upAction)
                , currentWidget(NULL)
                , panelFocus(panelFocus)
                , panelGrabbable(panelGrabbable)
                , scrollDirection(DOWN)
                , useMotion(true)
                , useDrag(true)
                , pressed(false)
                , drag(NULL)
        {}
        
    };
    
    class KeyboardSettings
    {
        public:
        Keyboard* keyboard;  // For keyboard state
        
        // Key mappings
        InputButtonSettings* up;
        InputButtonSettings* down;
        InputButtonSettings* left;
        InputButtonSettings* right;
        InputButtonSettings* leftClick;
        InputButtonSettings* middleClick;
        InputButtonSettings* rightClick;
        InputButtonSettings* scrollUp;
        InputButtonSettings* scrollDown;
        
        // Non-mouse keys (keyboard and joystick only)
        InputButtonSettings* cancel;
        InputButtonSettings* close;
        
        // Motion velocity
        float initVel;
        float vel;
        float velMax;
        float accel;
        
        float deaccelPeriod;  // If this is non-zero, the cursor is slowed down
        // after a certain amount of time, mimicking the way actual users use 
        // the cursor (making it so that they don't have to do this on their
        // own). (experimental)
        float deaccelMagnitude;  // M*sin(2*pi*t/T)
        
        
        // Holding a direction
        bool movingUp, movingDown, movingLeft, movingRight;
        
        
        KeyboardSettings()
                : up(NULL), down(NULL), left(NULL), right(NULL)
                , leftClick(NULL), middleClick(NULL), rightClick(NULL)
                , scrollUp(NULL), scrollDown(NULL)
                , cancel(NULL), close(NULL)
                , initVel(100)
                , velMax(600)
                , accel(500)
                , deaccelPeriod(0), deaccelMagnitude(4)
                , movingUp(0), movingDown(0), movingLeft(0), movingRight(0)
        {
            vel = initVel;
            
            keyboard = new Keyboard;
            up = new InputButtonSettings;
            up->button = SDLK_UP;
            down = new InputButtonSettings;
            down->button = SDLK_DOWN;
            left = new InputButtonSettings;
            left->button = SDLK_LEFT;
            right = new InputButtonSettings;
            right->button = SDLK_RIGHT;
            
            leftClick = new InputButtonSettings;
            leftClick->button = SDLK_SPACE;
            leftClick->downAction = ACTION_PRESS;
            leftClick->upAction = ACTION_ACTIVATE;
        }
        
    };
    
    class JoystickSettings
    {
        public:
        Joystick* joystick;  // For keyboard state
        int index;
        
        // Key mappings
        //InputButtonSettings* up;
        //InputButtonSettings* down;
        //InputButtonSettings* left;
        //InputButtonSettings* right;
        InputButtonSettings* leftClick;
        InputButtonSettings* middleClick;
        InputButtonSettings* rightClick;
        InputButtonSettings* scrollUp;
        InputButtonSettings* scrollDown;
        
        // Non-mouse keys (keyboard and joystick only)
        InputButtonSettings* cancel;
        InputButtonSettings* close;
        
        // Motion velocity
        float initVel;
        float vel;
        float velMax;
        float accel;
        
        bool softJoystick;
        
        float deaccelPeriod;  // If this is non-zero, the cursor is slowed down
        // after a certain amount of time, mimicking the way actual users use 
        // the cursor (making it so that they don't have to do this on their
        // own). (experimental)
        float deaccelMagnitude;  // M*sin(2*pi*t/T)
        
        
        // Holding a direction
        bool movingUp, movingDown, movingLeft, movingRight;
        
        
        JoystickSettings(SDL_Joystick* SDLjoy)
                : leftClick(NULL), middleClick(NULL), rightClick(NULL)
                , scrollUp(NULL), scrollDown(NULL)
                , cancel(NULL), close(NULL)
                , initVel(100)
                , velMax(600)
                , accel(500)
                , deaccelPeriod(0), deaccelMagnitude(4)
                , movingUp(0), movingDown(0), movingLeft(0), movingRight(0)
        {
            vel = initVel;
            
            joystick = new Joystick(SDLjoy);
            index = joystick->index;
            
            leftClick = new InputButtonSettings;
            leftClick->button = 0;
            leftClick->downAction = ACTION_PRESS;
            leftClick->upAction = ACTION_ACTIVATE;
        }
        
    };
    
    // Abstracts input for a mouse cursor so that any input method can do it
    // e.g. Mouse: Normal, Joystick: Like Maniac Mansion
    class PointerCursor : public Cursor
    {
        public:
        Mouse* mouse;
        JoystickSettings* joystick;
        KeyboardSettings* keyboard;
        
        SDL_Surface* image;
        
        bool updateHoverFocus;
        Widget* hoverFocus;  // This is notified with updates when the cursor is within its bounds.
        Widget* scrollFocus;
        Widget* buttonFocus;  // This is the currently active widget.
                              // It is told when the mouse moves.
        
        ScrollSelectEnum scrollSelectMode;
        
        InputButtonSettings leftClick;
        InputButtonSettings middleClick;
        InputButtonSettings rightClick;
        
        
        
        float fmx, fmy;
        int mx, my;
        int dx, dy;
        
        PointerCursor()
                : Cursor()
                , mouse(NULL)
                , joystick(NULL)
                , keyboard(NULL)
                , image(NULL)
                , updateHoverFocus(1)
                , hoverFocus(NULL)
                , scrollFocus(NULL)
                , buttonFocus(NULL)
                , scrollSelectMode(SCROLL_HOVER_FOCUS)
                , leftClick(ACTION_PRESS, ACTION_ACTIVATE, true, true)
                , middleClick(ACTION_NONE, ACTION_NONE)
                , rightClick(ACTION_NONE, ACTION_NONE)
                , fmx(0)
                , fmy(0)
                , mx(0)
                , my(0)
                , dx(0)
                , dy(0)
        {}
        PointerCursor(Mouse* mouse, SDL_Surface* image = NULL)
                : Cursor()
                , mouse(mouse)
                , joystick(NULL)
                , keyboard(NULL)
                , image(image)
                , updateHoverFocus(1)
                , hoverFocus(NULL)
                , scrollFocus(NULL)
                , buttonFocus(NULL)
                , scrollSelectMode(SCROLL_HOVER_FOCUS)
                , leftClick(ACTION_PRESS, ACTION_ACTIVATE, true, true)
                , middleClick(ACTION_NONE, ACTION_NONE)
                , rightClick(ACTION_NONE, ACTION_NONE)
                , fmx(0)
                , fmy(0)
                , mx(0)
                , my(0)
                , dx(0)
                , dy(0)
        {}
        PointerCursor(JoystickSettings* joystick, SDL_Surface* image = NULL)
                : Cursor()
                , mouse(NULL)
                , joystick(joystick)
                , keyboard(NULL)
                , image(image)
                , updateHoverFocus(1)
                , hoverFocus(NULL)
                , scrollFocus(NULL)
                , buttonFocus(NULL)
                , scrollSelectMode(SCROLL_HOVER_FOCUS)
                , leftClick(ACTION_PRESS, ACTION_ACTIVATE, true, true)
                , middleClick(ACTION_NONE, ACTION_NONE)
                , rightClick(ACTION_NONE, ACTION_NONE)
                , fmx(0)
                , fmy(0)
                , mx(0)
                , my(0)
                , dx(0)
                , dy(0)
        {}
        PointerCursor(KeyboardSettings* keyboard, SDL_Surface* image = NULL)
                : Cursor()
                , mouse(NULL)
                , joystick(NULL)
                , keyboard(keyboard)
                , image(image)
                , updateHoverFocus(1)
                , hoverFocus(NULL)
                , scrollFocus(NULL)
                , buttonFocus(NULL)
                , scrollSelectMode(SCROLL_HOVER_FOCUS)
                , leftClick(ACTION_PRESS, ACTION_ACTIVATE, true, true)
                , middleClick(ACTION_NONE, ACTION_NONE)
                , rightClick(ACTION_NONE, ACTION_NONE)
                , fmx(0)
                , fmy(0)
                , mx(0)
                , my(0)
                , dx(0)
                , dy(0)
        {
            
        }
        
        
        EventResult axisEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYAXISMOTION || joystick == NULL || event.jaxis.which != joystick->index || joystick->joystick == NULL || joystick->joystick->joystick == NULL)
                return EventResult();
            Uint8 axis = event.jaxis.axis;
            if(axis >= joystick->joystick->numAxes)
                return EventResult(NULL, true);
            joystick->joystick->axisValue[axis] = event.jaxis.value;
            // call widget function
            switch(joystick->joystick->axisAction[axis])
            {
                case ACTION_MOTION_LEFT:
                case ACTION_MOTION_RIGHT:
                    if(joystick->joystick->axisValue[axis] < -100)
                    {
                        joystick->movingLeft = true;
                        joystick->movingRight = false;
                    }
                    else if(joystick->joystick->axisValue[axis] > 100)
                    {
                        joystick->movingRight = true;
                        joystick->movingLeft = false;
                    }
                    else
                        joystick->movingLeft = joystick->movingRight = false;
                    return EventResult(NULL, true);
                case ACTION_MOTION_UP:
                case ACTION_MOTION_DOWN:
                    if(joystick->joystick->axisValue[axis] < -100)
                    {
                        joystick->movingUp = true;
                        joystick->movingDown = false;
                    }
                    else if(joystick->joystick->axisValue[axis] > 100)
                    {
                        joystick->movingDown = true;
                        joystick->movingUp = false;
                    }
                    else
                        joystick->movingUp = joystick->movingDown = false;
                    return EventResult(NULL, true);
                case ACTION_ACTIVATE:
                    return EventResult(buttonFocus, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult buttonEvent(const SDL_Event& event)
        {
            if((event.type != SDL_JOYBUTTONDOWN && event.type != SDL_JOYBUTTONUP) || joystick == NULL || joystick->joystick == NULL || joystick->joystick->joystick == NULL
               || event.jbutton.which != joystick->index)
                return EventResult();
            Uint8 button = event.jbutton.button;
            if(button >= joystick->joystick->numButtons)
                return EventResult(NULL, true);
            joystick->joystick->buttonValue[button] = event.jbutton.state;
            // call widget function
            switch(joystick->joystick->buttonAction[button])
            {
                case ACTION_DRAG:
                    return EventResult(buttonFocus, true);
                case ACTION_ACTIVATE:
                    return EventResult(buttonFocus, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult hatEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYHATMOTION || joystick == NULL || joystick->joystick == NULL || joystick->joystick->joystick == NULL || event.jhat.which != joystick->index)
                return EventResult();
            Uint8 hat = event.jhat.hat;
            if(hat >= joystick->joystick->numHats)
                return EventResult(NULL, true);
            joystick->joystick->hatValue[hat] = event.jhat.value;
            switch(joystick->joystick->hatAction[hat])
            // call widget function
            {
                case ACTION_DRAG:
                    return EventResult(buttonFocus, true);
                case ACTION_ACTIVATE:
                    return EventResult(buttonFocus, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult ballEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYBALLMOTION || joystick == NULL || joystick->joystick == NULL || joystick->joystick->joystick == NULL || event.jball.which != joystick->index)
                return EventResult();
            Uint8 ball = event.jball.ball;
            if(ball >= joystick->joystick->numBalls)
                return EventResult(NULL, true);
            joystick->joystick->ballRelX[ball] = event.jball.xrel;
            joystick->joystick->ballRelY[ball] = event.jball.yrel;
            // call widget function
            switch(joystick->joystick->ballAction[ball])
            {
                case ACTION_DRAG:
                    return EventResult(buttonFocus, true);
                case ACTION_ACTIVATE:
                    return EventResult(buttonFocus, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        virtual EventResult event(const SDL_Event& event);
        
        virtual void update();
        
        virtual void draw();
    };
    
    class NavNode;
    
    // Moves from widget to widget.
    // No mouse support
    class NavCursor : public Cursor
    {
        public:
        JoystickSettings* joystick;
        KeyboardSettings* keyboard;
        
        DesignPainter* design;
        
        Widget* currentWidget;
        NavNode* currentNode;
        Widget* scrollFocus;
        
        bool alwaysDraw;  // draw even when widget is hidden or folded
        
        NavCursor()
                : Cursor()
                , joystick(NULL)
                , keyboard(NULL)
                , design(NULL)
                , currentWidget(NULL)
                , currentNode(NULL)
                , scrollFocus(NULL)
                , alwaysDraw(false)
        {}
        NavCursor(JoystickSettings* joystick, DesignPainter* design = NULL)
                : Cursor()
                , joystick(joystick)
                , keyboard(NULL)
                , design(design)
                , currentWidget(NULL)
                , currentNode(NULL)
                , scrollFocus(NULL)
                , alwaysDraw(false)
        {}
        NavCursor(KeyboardSettings* keyboard, DesignPainter* design = NULL)
                : Cursor()
                , joystick(NULL)
                , keyboard(keyboard)
                , design(design)
                , currentWidget(NULL)
                , currentNode(NULL)
                , scrollFocus(NULL)
                , alwaysDraw(false)
        {}
        
        
        Widget* movement(DirectionEnum direction);
        
        EventResult axisEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYAXISMOTION || joystick == NULL || event.jaxis.which != joystick->index || joystick->joystick == NULL)
                return EventResult();
            Uint8 axis = event.jaxis.axis;
            if(axis >= joystick->joystick->numAxes)
                return EventResult(NULL, true);
            joystick->joystick->axisValue[axis] = event.jaxis.value;
            // call widget function
            switch(joystick->joystick->axisAction[axis])
            {
                case ACTION_DRAG:
                    return EventResult(currentWidget, true);
                case ACTION_ACTIVATE:
                    return EventResult(currentWidget, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult buttonEvent(const SDL_Event& event)
        {
            if((event.type != SDL_JOYBUTTONDOWN && event.type != SDL_JOYBUTTONUP) || joystick == NULL || joystick->joystick == NULL
               || event.jbutton.which != joystick->index)
                return EventResult();
            Uint8 button = event.jbutton.button;
            if(button >= joystick->joystick->numButtons)
                return EventResult(NULL, true);
            joystick->joystick->buttonValue[button] = event.jbutton.state;
            // call widget function
            switch(joystick->joystick->buttonAction[button])
            {
                case ACTION_DRAG:
                    return EventResult(currentWidget, true);
                case ACTION_ACTIVATE:
                    return EventResult(currentWidget, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult hatEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYHATMOTION || joystick == NULL || joystick->joystick == NULL || event.jhat.which != joystick->index)
                return EventResult();
            Uint8 hat = event.jhat.hat;
            if(hat >= joystick->joystick->numHats)
                return EventResult(NULL, true);
            joystick->joystick->hatValue[hat] = event.jhat.value;
            // call widget function
            switch(joystick->joystick->hatAction[hat])
            {
                case ACTION_DRAG:
                    return EventResult(currentWidget, true);
                case ACTION_ACTIVATE:
                    return EventResult(currentWidget, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult ballEvent(const SDL_Event& event)
        {
            if(event.type != SDL_JOYBALLMOTION || joystick == NULL || joystick->joystick == NULL || event.jball.which != joystick->index)
                return EventResult();
            Uint8 ball = event.jball.ball;
            if(ball >= joystick->joystick->numBalls)
                return EventResult(NULL, true);
            joystick->joystick->ballRelX[ball] = event.jball.xrel;
            joystick->joystick->ballRelY[ball] = event.jball.yrel;
            // call widget function
            switch(joystick->joystick->ballAction[ball])
            {
                case ACTION_DRAG:
                    return EventResult(currentWidget, true);
                case ACTION_ACTIVATE:
                    return EventResult(currentWidget, true);
                case ACTION_CANCEL:
                    ;  // go up a level from current widget?
                    return EventResult(NULL, true);
                case ACTION_SCROLL:
                    return EventResult(scrollFocus, true);
                default:
                    return EventResult(NULL, true);
            }
        }
        
        EventResult event(const SDL_Event& event);
        
        virtual void update()
        {}
        
        virtual void draw();
        
    };
    
    
    
    
}

#endif
